﻿using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using Server=Microsoft.VisualStudio.WebHost.Server;
using Microsoft.Win32;
using log4net;

namespace Objectware.Silverlight.Testing.Runner
{
	public class ServerManager
	{
		public static readonly ServerManager Instance = new ServerManager();

		private readonly ILog logger;

		private const string SilverlightUnitTestRunnerPath = "SilverlightUnitTestRunner";

		private const string PhysicalPath_RegistryValue = "WebSitePath";
		private const string PhysicalPath_RegistryKey = "Software\\Objectware\\" + SilverlightUnitTestRunnerPath;

		private Server _server;


		private ServerManager()
		{
			this.logger = Logging.GetLogger<ServerManager>();
			this.InitializeServer();
		}
		

		private static RegistryKey GetPhysicalPathKey()
		{
			var key = Registry.LocalMachine.OpenSubKey(PhysicalPath_RegistryKey,true) ??
			          Registry.LocalMachine.CreateSubKey(PhysicalPath_RegistryKey);
			return key;
		}

		private static bool IsPhysicalWebSitePathSet()
		{
			var physicalPath = GetPhysicalWebSitePath();
			return !string.IsNullOrEmpty(physicalPath);
		}

		private static void SetPhysicalWebSitePath()
		{
			var form = new SelectWebSitePathForm();
			form.ShowDialog();
			if( string.IsNullOrEmpty(form.SelectedPath) )
			{
				MessageBox.Show(
					"You didn't select a path. This will cause tests to fail.\n The next time you start the plugin, you will be asked again.");
			} else
			{
				var key = GetPhysicalPathKey();
				key.SetValue(PhysicalPath_RegistryValue,form.SelectedPath);
			}
		}

		private static string GetPhysicalWebSitePath()
		{
			var key = GetPhysicalPathKey();

			var physicalPath = key.GetValue(PhysicalPath_RegistryValue, string.Empty) as string;
			return physicalPath;			
		}

		public static string HandlePhysicalWebSitePath()
		{
			if( !IsPhysicalWebSitePathSet() )
			{
				SetPhysicalWebSitePath();
			}

			var physicalPath = GetPhysicalWebSitePath();

			return physicalPath;
		}


		private void InitializeServer()
		{
			logger.Info("Setup server");

			var physicalPath = HandlePhysicalWebSitePath();

			logger.Info("Physical : " + physicalPath);

			try
			{
				if (!Directory.Exists(physicalPath))
				{
					Directory.CreateDirectory(physicalPath);
				}

				this._server = new Server(1024, "/", physicalPath,false);

				logger.Info("Server created");
				this._server.Start();

				logger.Info(string.Format("Server started on port : {0}",this._server.Port));
			}
			catch (Exception ex)
			{
				logger.Fatal("Error occured during setup of server : " + ex.Message);
			}
		}

		public string VirtualPath { get { return this._server.VirtualPath; } }
		public string PhysicalPath { get { return this._server.PhysicalPath; } }
		public int Port { get { return this._server.Port; } }

		public string BaseUrl
		{
			get
			{
				var builder = new StringBuilder();

				builder.AppendFormat("http://localhost:{0}", this.Port);
				if (this.VirtualPath.Length > 1)
				{
					builder.Append(this.VirtualPath);
				}
				return builder.ToString();
			}
		}

		public string TestResultServiceUrl
			{
			get
			{
				return BaseUrl + "/Services/TestResultService.svc";
			}
		}


		public string VirtualTestPath { get { return this.VirtualPath + SilverlightUnitTestRunnerPath; } }
		public string PhysicalTestPath { get { return this.PhysicalPath + SilverlightUnitTestRunnerPath; } }

		public string TestPageUrl
		{
			get
			{
				var builder = new StringBuilder();

				builder.AppendFormat("http://localhost:{0}", this.Port);
				if (this.VirtualTestPath.Length > 1)
				{
					builder.Append(this.VirtualTestPath);
				}
				builder.Append("/TestPage.html");

				return builder.ToString();
			}
		}

		public void ClearTestDirectory()
		{
			var files = Directory.GetFiles(this.PhysicalTestPath, "*.*", SearchOption.AllDirectories);
			foreach (var file in files)
			{
				File.Delete(file);
			}
		}

		public void PrepareTestDirectory(TestHarness harness)
		{

			logger.Info("PrepareTestDirectory()");
			var projectPath = Path.GetDirectoryName(harness.XapFilePath);
			logger.Info(string.Format("Project path {0}", projectPath));
			var sourceFiles = Directory.GetFiles(projectPath, "*.*", SearchOption.AllDirectories);

			foreach (var file in sourceFiles)
			{
				var actualFile = file.Substring(projectPath.Length + 1);
				var directory = Path.GetDirectoryName(actualFile);
				var destinationDirectory = this.PhysicalTestPath + "\\"+ directory;
				

				logger.Info(string.Format("Copy file '{0}' ",file));
				logger.Info(string.Format("Actual file '{0}'",actualFile));
				logger.Info(string.Format("Directory '{0}'",directory));
				logger.Info(string.Format("DestinationDirectory '{0}'", destinationDirectory));


				if (!Directory.Exists(destinationDirectory))
				{
					logger.Info("Destination Directory does not exist - create");
					Directory.CreateDirectory(destinationDirectory);
				}

				var destinationFile = this.PhysicalTestPath + "\\" + actualFile;
				if (File.Exists(destinationFile))
				{
					logger.Info("File already exists - delete it");
					File.Delete(destinationFile);
				}
				File.Copy(file, destinationFile);
			}
		}

		public void PrepareTestRunner(TestHarness harness)
		{
			string destinationXap = string.Format("{0}\\Objectware.Silverlight.Testing.TestProject.xap", this.PhysicalTestPath);

			File.WriteAllBytes(destinationXap, Resources.TestProject);
			string destinationTestPage = string.Format("{0}\\TestPage.html", this.PhysicalTestPath);

			File.WriteAllText(destinationTestPage, Resources.TestPage);
		}
	}
}
